!theme mono

skinparam classAttributeIconSize 0
skinparam classFontSize 12
skinparam classAttributeFontSize 11

title HealthSync 역설계 - Health Service 데이터 설계서

package "Health Service Database Schema" as health_db #lightblue {

  entity "health_checkups" as health_checkup {
    * id : BIGINT <<PK>>
    --
    * user_id : VARCHAR(50)
    * member_serial_number : BIGINT
    * raw_id : BIGINT <<FK>>
    * reference_year : INTEGER
    * height_cm : DECIMAL(5,2)
    * weight_kg : DECIMAL(5,2)
    * waist_cm : DECIMAL(5,2)
    * bmi : DECIMAL(4,2)
    * visual_acuity_left : DECIMAL(3,1)
    * visual_acuity_right : DECIMAL(3,1)
    * hearing_avg : DECIMAL(4,1)
    * systolic_bp : INTEGER
    * diastolic_bp : INTEGER
    * fasting_glucose : INTEGER
    * total_cholesterol : INTEGER
    * triglyceride : INTEGER
    * hdl_cholesterol : INTEGER
    * ldl_cholesterol : INTEGER
    * hemoglobin : DECIMAL(4,1)
    * urine_protein : INTEGER
    * serum_creatinine : DECIMAL(4,1)
    * ast : INTEGER
    * alt : INTEGER
    * gamma_gtp : INTEGER
    * smoking_status : INTEGER
    * drinking_status : INTEGER
    * risk_level : VARCHAR(20)
    * abnormal_indicators : JSON
    * health_score : INTEGER
    * created_at : TIMESTAMP
    * updated_at : TIMESTAMP
    --
    + 인덱스: idx_user_id_year
    + 인덱스: idx_member_serial_number
    + 인덱스: idx_risk_level
    + 인덱스: idx_health_score
  }

  entity "health_checkup_raw" as health_checkup_raw {
    * raw_id : BIGINT <<PK>>
    --
    * member_serial_number : BIGINT
    * reference_year : INTEGER
    * birth_date : DATE
    * name : VARCHAR(50)
    * region_code : INTEGER
    * gender_code : INTEGER
    * age : INTEGER
    * height : INTEGER
    * weight : INTEGER
    * waist_circumference : INTEGER
    * visual_acuity_left : DECIMAL(3,1)
    * visual_acuity_right : DECIMAL(3,1)
    * hearing_left : INTEGER
    * hearing_right : INTEGER
    * systolic_bp : INTEGER
    * diastolic_bp : INTEGER
    * fasting_glucose : INTEGER
    * total_cholesterol : INTEGER
    * triglyceride : INTEGER
    * hdl_cholesterol : INTEGER
    * ldl_cholesterol : INTEGER
    * hemoglobin : DECIMAL(4,1)
    * urine_protein : INTEGER
    * serum_creatinine : DECIMAL(4,1)
    * ast : INTEGER
    * alt : INTEGER
    * gamma_gtp : INTEGER
    * smoking_status : INTEGER
    * drinking_status : INTEGER
    * created_at : TIMESTAMP
    --
    + 인덱스: idx_member_serial_year
    + 인덱스: idx_name_birthdate
    + 인덱스: idx_gender_age
  }

  entity "health_normal_ranges" as health_normal_range {
    * item_code : VARCHAR(20) <<PK>>
    * gender_code : INTEGER <<PK>>
    --
    * item_name : VARCHAR(100)
    * normal_min : DECIMAL(10,2)
    * normal_max : DECIMAL(10,2)
    * caution_min : DECIMAL(10,2)
    * caution_max : DECIMAL(10,2)
    * danger_min : DECIMAL(10,2)
    * danger_max : DECIMAL(10,2)
    * unit : VARCHAR(20)
    * description : TEXT
    * is_active : BOOLEAN
    * created_at : TIMESTAMP
    * updated_at : TIMESTAMP
    --
    + 인덱스: idx_item_code
    + 인덱스: idx_gender_code
  }

  entity "health_files" as health_file {
    * file_id : VARCHAR(50) <<PK>>
    --
    * user_id : VARCHAR(50)
    * file_name : VARCHAR(255)
    * file_type : VARCHAR(50)
    * file_url : VARCHAR(500)
    * file_size : BIGINT
    * upload_status : VARCHAR(20)
    * uploaded_at : TIMESTAMP
    * processed_at : TIMESTAMP
    --
    + 인덱스: idx_user_id
    + 인덱스: idx_upload_status
    + 인덱스: idx_uploaded_at
  }

  note right of health_checkup
    **가공된 건강검진 데이터**
    • User Service의 사용자와 연결
    • 정상치 기준과 비교 분석 완료
    • 건강 점수 및 위험도 계산 결과
    • 이상 항목을 JSON 배열로 저장
    • 1명당 1개 레코드 (최신 데이터만)
  end note

  note right of health_checkup_raw
    **건강보험공단 원본 데이터**
    • 연도별 검진 데이터 보관
    • 가공 전 원본 데이터 유지
    • 개인정보 포함 (이름, 생년월일)
    • 성별/나이별 통계 분석 용도
    • 여러 연도 데이터 보관 가능
  end note

  note right of health_normal_range
    **건강검진 정상치 기준**
    • 성별별 정상치 기준 관리
    • gender_code: 0(공통), 1(남성), 2(여성)
    • 정상/주의/위험 3단계 범위
    • 의료진 검토 후 업데이트
    • 마스터 데이터 성격
  end note

  note right of health_file
    **업로드된 건강검진 파일**
    • Azure Blob Storage 연동
    • PDF, 이미지 파일 지원
    • 업로드 상태 추적
    • OCR 처리 결과 연동 준비
  end note
}

package "관계 정의" as relationships {
  health_checkup ||--|| health_checkup_raw : raw_id
  health_checkup }|--|| health_normal_range : 정상치_비교
  health_file }|--|| health_checkup : 파일_연동
  
  note as n1
    **외래키 관계**
    health_checkups.raw_id → health_checkup_raw.raw_id
    
    **논리적 관계**
    • health_checkups ↔ health_normal_ranges (정상치 비교)
    • health_files ↔ health_checkups (파일-데이터 연결)
    • User Service users ↔ health_checkups (사용자별 데이터)
    
    **참조 무결성**
    • raw_id는 NOT NULL (원본 데이터 필수)
    • user_id는 User Service와 일관성 유지
    • member_serial_number로 건보공단 데이터 연결
  end note
}

package "데이터 타입 및 제약조건" as constraints {
  note as n2
    **health_checkups 제약조건**
    • id: AUTO_INCREMENT
    • user_id: 최대 50자, NOT NULL
    • member_serial_number: User Service와 일치
    • reference_year: 1990~현재년도
    • bmi: 계산값, 10.0~50.0 범위
    • blood pressure: systolic > diastolic
    • risk_level: 'normal', 'caution', 'danger'
    • health_score: 0~100 점수
    • abnormal_indicators: JSON 배열 형태
    
    **health_checkup_raw 제약조건**
    • raw_id: AUTO_INCREMENT
    • member_serial_number: NOT NULL
    • reference_year: NOT NULL
    • gender_code: 1(남성), 2(여성)
    • age: 0~120 범위
    • 모든 수치 데이터: 음수 불가
    
    **health_normal_ranges 제약조건**
    • 복합 기본키: (item_code, gender_code)
    • gender_code: 0(공통), 1(남성), 2(여성)
    • normal_min ≤ normal_max
    • caution 범위는 normal 범위 밖
    • danger 범위는 caution 범위 밖
    
    **health_files 제약조건**
    • file_id: UUID 형태
    • file_type: 'pdf', 'jpg', 'png' 등
    • file_size: 바이트 단위, 최대 10MB
    • upload_status: 'uploading', 'completed', 'failed'
  end note
}

package "정상치 기준 예시 데이터" as normal_ranges_data {
  note as n3
    **주요 항목별 정상치 기준 (성인 남성 기준)**
    
    | item_code | normal_range | caution_range | danger_range |
    |-----------|-------------|---------------|--------------|
    | BMI | 18.5~24.9 | 25.0~29.9 또는 <18.5 | ≥30.0 |
    | SYSTOLIC_BP | 90~119 | 120~139 | ≥140 |
    | DIASTOLIC_BP | 60~79 | 80~89 | ≥90 |
    | FASTING_GLUCOSE | 70~99 | 100~125 | ≥126 |
    | TOTAL_CHOLESTEROL | <200 | 200~239 | ≥240 |
    | HDL_CHOLESTEROL | ≥40 | 35~39 | <35 |
    | LDL_CHOLESTEROL | <130 | 130~159 | ≥160 |
    | TRIGLYCERIDE | <150 | 150~199 | ≥200 |
    | AST | <40 | 40~80 | >80 |
    | ALT | <40 | 40~80 | >80 |
    | GAMMA_GTP | <60 | 60~100 | >100 |
    | HEMOGLOBIN | 13.0~17.0 | 12.0~12.9 | <12.0 |
    
    **성별 차이**
    • 여성은 HDL 콜레스테롤 ≥50, 혈색소 12.0~15.0
    • 임신 가능 연령대 여성은 별도 기준 적용
  end note
}

package "성능 및 운영 고려사항" as performance {
  note as n4
    **인덱스 전략**
    • health_checkups: (user_id, reference_year) 복합 인덱스
    • health_checkup_raw: (member_serial_number, reference_year) 복합 인덱스
    • health_normal_ranges: item_code 단일 인덱스
    • health_files: (user_id, uploaded_at) 복합 인덱스
    
    **파티셔닝**
    • health_checkup_raw: reference_year 기준 연도별 파티셔닝
    • health_files: uploaded_at 기준 월별 파티셔닝
    
    **아카이빙**
    • 5년 이상 된 raw 데이터는 별도 아카이브 테이블로 이관
    • 업로드 실패 파일은 30일 후 자동 삭제
    
    **캐싱 전략**
    • health_normal_ranges: Redis에 전체 캐싱 (24시간)
    • 사용자별 최신 건강검진: Redis 캐싱 (1시간)
    • 건강 점수 계산 결과: Redis 캐싱 (6시간)
    
    **모니터링 지표**
    • 정상치 기준 업데이트 빈도
    • 평균 건강 점수 추이
    • 위험군 사용자 비율
    • 파일 업로드 성공률
  end note
}

package "데이터 보안 및 규정 준수" as security {
  note as n5
    **개인정보 보호**
    • health_checkup_raw.name: 암호화 저장
    • health_checkup_raw.birth_date: 마스킹 처리
    • 의료 데이터 접근 시 감사 로그 기록
    
    **의료정보 보안**
    • 의료법 및 개인정보보호법 준수
    • 건강검진 데이터 3년 보존 의무
    • 개인식별정보와 건강정보 분리 저장
    
    **접근 제어**
    • 의료진만 정상치 기준 수정 가능
    • 사용자 본인 데이터만 조회 가능
    • 관리자 접근 시 의료진 승인 필요
    
    **데이터 무결성**
    • 건강검진 데이터 변조 방지
    • 원본 데이터 불변성 보장
    • 정상치 기준 변경 시 이력 관리
    
    **백업 및 복구**
    • 건강검진 데이터 일일 백업
    • 정상치 기준 변경 전 백업
    • 재해 복구 시 의료진 검증 필수
  end note
}